package core;

import java.io.File;
import java.io.IOException;
import java.util.*;

import gitobject.*;
import repository.Repository;

public class JitDiff {

    public static void diffBranch(String branchName1, String branchName2, boolean stat, boolean print) throws IOException{
        String commitId1 = JitBranch.getCommitId(branchName1);
        String commitId2 = JitBranch.getCommitId(branchName2);
        if(commitId1 == null || Objects.equals(commitId1, "")) {
            System.out.println("fatal: unknown branch '" + branchName1 + "'.");
            return;
        }
        if(commitId2 == null || Objects.equals(commitId2, "")) {
            System.out.println("fatal: unknown branch '" + branchName2 + "'.");
            return;
        }
        diffCommit(commitId1, commitId2, branchName1, branchName2, stat, print);
    }

    public static void diffCommit(String commitId1, String commitId2, String name1, String name2, boolean stat, boolean print) throws IOException{
        String diffContent = "";
        Map<String, String> commitMap1 = new HashMap<>();
        Map<String, String> commitMap2 = new HashMap<>();
        JitMap.readAllCommit(commitMap1, commitId1, false);
        JitMap.readAllCommit(commitMap2, commitId2, false);
        for (Map.Entry<String, String> item : commitMap1.entrySet()) {
            String keyPath = item.getKey();
            String newPath = keyPath.replace(Tree.treeSeparator, File.separator);
            String key1 = commitMap1.get(keyPath);
            if(commitMap2.containsKey(keyPath)){
                String key2 = commitMap2.get(keyPath);
                if(!Objects.equals(key1, key2)){
                    diffContent += "a->\tkey:\t" + key1 + "\n";
                    diffContent += "a->\tblob on " + name1 + ":\t'" + newPath + "'\n";
                    diffContent += "b->\tkey:\t" + key2 + "\n";
                    diffContent += "b->\tblob on " + name2 + ":\t'" + newPath + "'\n";
                    if(stat){
                        diffContent += "\ta -> b modified\n";
                    }
                    else{
                        diffContent += "\tchange of a -> b\n";
                        Blob blob1 = Blob.deserialize(key1);
                        Blob blob2 = Blob.deserialize(key2);
                        if (blob1 == null) {
                            System.out.println("fatal: unknown blob '" + key1 + "'.");
                            continue;
                        }
                        if (blob2 == null) {
                            System.out.println("fatal: unknown blob '" + key2 + "'.");
                            continue;
                        }
                        String s1 = blob1.getValue();
                        String s2 = blob2.getValue();
                        diffContent += diff(s1, s2);
                    }
                    diffContent += "\n";
                }
            }
            else{
                diffContent += "a->\tkey:\t" + key1 + "\n";
                diffContent += "a->\tblob on " + name1 + ":\t'" + newPath + "'\n";
                diffContent += "b->\tblob on " + name2 + ":\tfile deleted\t'" + newPath + "'\n";
                diffContent += "\n";
            }
        }
        for (Map.Entry<String, String> item : commitMap2.entrySet()) {
            String keyPath = item.getKey();
            String newPath = keyPath.replace(Tree.treeSeparator, File.separator);
            String key2 = commitMap2.get(keyPath);
            if(!commitMap1.containsKey(keyPath)) {
                diffContent += "b->\tkey:\t" + key2 + "\n";
                diffContent += "b->\tblob on " + name2 + ":\tnew file\t'" + newPath + "'\n";
            }
        }
        if(print)System.out.println(diffContent);
    }

    public static void diffWorkSpaceWithMap(String commitMapOrIndexMap) throws IOException{
        String diffContent = "";
        Set<Map.Entry<String, String>> items;
        final int commitMapType = 1;
        final int indexMapType = 2;
        int type = 0;
        if(Objects.equals(commitMapOrIndexMap, "commitMap")) {
            items = JitMap.commitMap.entrySet();
            type = commitMapType;
        }
        else if(Objects.equals(commitMapOrIndexMap, "indexMap")){
            items = JitMap.index.getMapValues();
            type = indexMapType;
        }
        else items = new HashSet<>();
        for (Map.Entry<String, String> item : items) {
            String keyPath = item.getKey();
            String newPath = keyPath.replace(Tree.treeSeparator, File.separator);
            File workSpaceBlob = new File(Repository.getWorkTree() + File.separator + newPath);
            String indexKey;
            if(workSpaceBlob.exists()){
                try {
                    if(type == commitMapType)
                        indexKey = JitMap.commitMap.get(keyPath);
                    else
                        indexKey = JitMap.index.getIndexKey(keyPath);
                    Blob blob = new Blob(workSpaceBlob);
                    if(!Objects.equals(blob.getKey(), indexKey)){
                        if(type == commitMapType) {
                            diffContent += "a->\tkey:\t" + indexKey + "\n";
                            diffContent += "a->\tblob committed:\t'" + newPath + "'\n";
                        }
                        else {
                            diffContent += "a->\tkey:\t" + indexKey + "\n";
                            diffContent += "a->\tblob added:\t'" + newPath + "'\n";
                        }
                        diffContent += "b->\tkey:\t" + blob.getKey() + "\n";
                        diffContent += "b->\tin workspace:\t'" + newPath + "'\n";
                        diffContent += "\tchange of a -> b\n";
                        Blob indexBlob = Blob.deserialize(indexKey);
                        assert indexBlob != null;
                        String s1 = indexBlob.getValue();
                        String s2 = blob.getValue();
                        diffContent += diff(s1, s2);
                        diffContent += "\n";
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            else{
                diffContent += "b->\tin workspace:\tfile deleted\t'" + newPath + "'\n";
                diffContent += "\n";
            }
        }
        System.out.println(diffContent);
    }

    public static void diffIndexAndCommit(String commitId){
        Map<String, String> commitMap = new HashMap<>();
        JitMap.readAllCommit(commitMap, commitId, false);
        try {
            diffIndexAndCommitMap(commitMap);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void diffIndexAndCommitMap(Map<String, String> treeMap) throws IOException{
        String diffContent = "";
        for (Map.Entry<String, String> item : JitMap.index.getMapValues()) {
            String keyPath = item.getKey();
            String newPath = keyPath.replace(Tree.treeSeparator, File.separator);
            String addedKey = JitMap.index.getIndexKey(keyPath);
            if(treeMap.containsKey(keyPath)){
                String commitKey = treeMap.get(keyPath);
                if(!Objects.equals(commitKey, addedKey)){
                    diffContent += "a->\tkey:\t" + commitKey + "\n";
                    diffContent += "a->\tblob committed:\t'" + newPath + "'\n";
                    diffContent += "b->\tkey:\t" + addedKey + "\n";
                    diffContent += "b->\tblob added:\t'" + newPath + "'\n";
                    diffContent += "\tchange of a -> b\n";
                    Blob commitBlob = Blob.deserialize(commitKey);
                    Blob indexBlob = Blob.deserialize(addedKey);
                    if (commitBlob == null) {
                        System.out.println("fatal: unknown blob '" + commitKey + "'.");
                        continue;
                    }
                    if (indexBlob == null) {
                        System.out.println("fatal: unknown blob '" + addedKey + "'.");
                        continue;
                    }
                    String s1 = commitBlob.getValue();
                    String s2 = indexBlob.getValue();
                    diffContent += diff(s1, s2);
                    diffContent += "\n";
                }
            }
            else{
                diffContent += "b->\tkey:\t" + addedKey + "\n";
                diffContent += "b->\tnew file:\t'" + newPath + "'\n";
                diffContent += "\n";
            }
        }
        System.out.println(diffContent);
    }

    public static String diff(String s1, String s2){
        if(s1 == null) s1 = "";
        if(s2 == null) s2 = "";
        String[] lines1 = s1.split("\n");
        String[] lines2 = s2.split("\n");
        int len1 = lines1.length, len2 = lines2.length;
        boolean[][] value_table = new boolean[len1][len2];
        for(int i=0; i<len1; i++){
            for(int j=0; j<len2; j++){
                value_table[i][j] = Objects.equals(lines1[i], lines2[j]);
            }
        }
        Queue<DiffNode> q = new LinkedList<>();
        q.add(new DiffNode());
        while(!q.isEmpty()){
            DiffNode uNode = q.poll();
            if(uNode.x == len1 && uNode.y == len2)
                return makeMovePath(uNode, lines1, lines2);
            if(uNode.x < len1 && uNode.y < len2 && value_table[uNode.x][uNode.y])
                q.add(new DiffNode(uNode, uNode.value, uNode.x + 1, uNode.y + 1));
            if(uNode.x < len1)
                q.add(new DiffNode(uNode, uNode.value + 1, uNode.x + 1, uNode.y));
            if(uNode.y < len2)
                q.add(new DiffNode(uNode, uNode.value + 1, uNode.x, uNode.y + 1));
        }
        return "";
    }

    public static String makeMovePath(DiffNode node, String[] lines1, String[] lines2){
        String diffResults = "";
        if(node == null)return "";
        int len = node.preNode.size();
        for(int i=0; i<len-1; i++){
            DiffNode node1 = node.preNode.get(i);
            DiffNode node2 = node.preNode.get(i+1);
            if(node2.x == node1.x + 1 && node2.y == node1.y + 1) {
                continue;
            }
            else if(node2.x == node1.x + 1){
                String name = String.format("%-9s", "(" + (node1.x + 1) + ")");
                diffResults += name + "-\t" + lines1[node1.x] + "\n";
            }
            else if(node2.y == node1.y + 1){
                String name = String.format("%-9s", "(" + (node1.x) + ")");
                diffResults += name + "+\t" + lines2[node1.y] + "\n";
            }
        }
        return diffResults;
    }
}
